using System;
using System.Collections.Generic;
using System.Text;

namespace SharpMud.Net.Intermud3
{
    public abstract class PacketBase
    {
        protected SharpMud.LPC.Array _data;

        #region Constants For Lookup
        public const int INDEX_PacketType = 0;
        public const int INDEX_TTL = 1;
        public const int INDEX_OriginatorMudname = 2;
        public const int INDEX_OriginatorUsername = 3;
        public const int INDEX_TargetMudname = 4;
        public const int INDEX_TargetUsername = 5;
        #endregion

        #region Constructors
        protected PacketBase()
        {
            _data = new LPC.Array();
        }
        protected  PacketBase(params LPC.Object[] data)
            :this(new LPC.Array(data))
        {
        }
        protected PacketBase(LPC.Array data)
        {
            if (data.Count < 6)
                throw new InvalidPacketException("There is not enough data in the given Array to create a GenericPacket.");
            _data = data;
        }
        #endregion

        #region Public Properties
        public virtual string PacketType
        {
            get
            {
                return (string)((LPC.String)(_data[INDEX_PacketType]));
            }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");
                if (_data[INDEX_PacketType] is LPC.String)
                {
                    ((LPC.String)_data[INDEX_PacketType]).Value = value;
                    return;
                }
                else if (_data[INDEX_PacketType] == null)
                {
                    _data[INDEX_PacketType] = new LPC.String(value);
                    return;
                }
                throw new InvalidOperationException();
            }
        }

        public virtual int TimeToLive
        {
            get
            {
                return (int)((LPC.Integer)(_data[INDEX_TTL]));
            }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");
                if (_data[INDEX_TTL] is LPC.Integer)
                {
                    ((LPC.Integer)_data[INDEX_TTL]).Value = value;
                    return;
                }
                else if (_data[INDEX_TTL] == null)
                {
                    _data[INDEX_TTL] = new SharpMud.LPC.Integer(value);
                    return;
                }
                throw new InvalidOperationException();
            }
        }

        public virtual string OriginatorMudname
        {
            get
            {
                return (string)((LPC.String)(_data[INDEX_OriginatorMudname]));
            }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");
                if (_data[INDEX_OriginatorMudname] is LPC.String)
                {
                    ((LPC.String)_data[INDEX_OriginatorMudname]).Value = value;
                    return;
                }
                else if (_data[INDEX_OriginatorMudname] == null)
                {
                    _data[INDEX_OriginatorMudname] = new LPC.String(value);
                    return;
                }
                throw new InvalidOperationException();
            }
        }

        public virtual string OriginatorUsername
        {
            get
            {
                return (string)((LPC.String)(_data[INDEX_OriginatorUsername]));
            }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");
                if (_data[INDEX_OriginatorUsername] is LPC.String)
                {
                    ((LPC.String)_data[INDEX_OriginatorUsername]).Value = value;
                    return;
                }
                else if (_data[INDEX_OriginatorUsername] == null)
                {
                    _data[INDEX_OriginatorUsername] = new LPC.String(value);
                    return;
                }
                throw new InvalidOperationException();
            }
        }

        public virtual string TargetMudname
        {
            get
            {
                return (string)((LPC.String)(_data[INDEX_TargetMudname]));
            }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");
                if (_data[INDEX_TargetMudname] is LPC.String)
                {
                    ((LPC.String)_data[INDEX_TargetMudname]).Value = value;
                    return;
                }
                else if (_data[INDEX_TargetMudname] == null)
                {
                    _data[INDEX_TargetMudname] = new LPC.String(value);
                    return;
                }
                throw new InvalidOperationException();
            }
        }
        public virtual string TargetUsername
        {
            get
            {
                return (string)((LPC.String)(_data[INDEX_TargetUsername]));
            }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");
                if (_data[INDEX_TargetUsername] is LPC.String)
                {
                    ((LPC.String)_data[INDEX_TargetUsername]).Value = value;
                    return;
                }
                else if (_data[INDEX_TargetUsername] == null)
                {
                    _data[INDEX_TargetUsername] = new LPC.String(value);
                    return;
                }
                throw new InvalidOperationException();
            }
        }
        #endregion

        #region Public Methods
        private static Dictionary<string,PacketCreator> _PacketCreators;
        static PacketBase()
        {
            _PacketCreators = new Dictionary<string, PacketCreator>();
            _PacketCreators.Add( "tell", Packets.Tell.CreatePacket );
        }

        public MudModePacket ToMudModePacket()
        {
            return new MudModePacket(System.Text.Encoding.ASCII.GetBytes(this._data.ToParsableString()));
        }

        public static PacketBase FromLPCArray(LPC.Array lpcArray)
        {
            PacketCreator packetCreator;
            
            string packetType = ((LPC.String)lpcArray[PacketBase.INDEX_PacketType]).Value.ToString().Trim().ToLower();
            bool packetCreatorExists = _PacketCreators.TryGetValue(packetType,out packetCreator);
            System.Diagnostics.Debug.Assert(packetCreatorExists == (packetCreator != null), "packetCreatorExists==(packetCreator!=null)");

            if (!packetCreatorExists)
                return GenericPacket.CreatePacket(lpcArray);

            PacketBase p = _PacketCreators[((LPC.String)lpcArray[PacketBase.INDEX_PacketType]).Value.ToString()](lpcArray);
            return p;
        }

        public static PacketBase FromMudModePacket(MudModePacket binaryPacket)
        {
            byte[] data = binaryPacket.ToByteArray();
            string dataString = System.Text.Encoding.ASCII.GetString(data);
            LPC.Object[] lpcData = LPC.Object.Parse(dataString);
            foreach (LPC.Object lpcDatum in lpcData)
            {
                if (lpcDatum is LPC.Array)
                    return PacketBase.FromLPCArray(lpcDatum as LPC.Array);
            }
            return null;
        }
        #endregion

        #region Static Methods

        #region Conversion Operators
        public static explicit operator SharpMud.Net.MudModePacket(PacketBase val)
        {
            if (val == null)
                throw new ArgumentNullException("val", "Attempt to convert a null Intermud3 packet to a MudModePacket.");
            return val.ToMudModePacket();
        }
        

        public static explicit operator SharpMud.LPC.Array(PacketBase val)
        {
            if (val == null)
                throw new ArgumentNullException("val", "Attempt to convert a null Intermud3 packet to an LPC Array.");
            return (SharpMud.LPC.Array)((ICloneable)val._data).Clone();
        }
        public static explicit operator PacketBase(SharpMud.LPC.Array val)
        {
            if (val == null)
                throw new ArgumentNullException("val", "Attempt to convert a null LPC Array to an Intermud3 packet.");
            if (val.Count <= 0)
                throw new ArgumentException("Attempt to convert an empty LPC Array to an Intermud3 packet.","val");
            return PacketBase.FromLPCArray(val);
        }
        #endregion

        #endregion
    }

    public delegate PacketBase PacketCreator(LPC.Array lpcData);
}
